<?php


require_once 'Zend/Application.php';  


class Kernel extends Zend_Application
{
    /**
     * Singleton instance
     */
    private static $_instance = null;


	private $_siteName = "Euso";
	private $_siteTagline = "Social networking platform";
	private $_siteFooterNotice = "Powered by Euso";

	private $_headTitle = NULL;


	private $_moduleList = array();
	private $_widgetList = array();


	private $_dbInitialized = FALSE;
	private $_optionsDbLoaded = FALSE;


	/**
	 * Initialize the application.
	 *
	 * @return void
	 **/
	public function __construct($environment = null, $options = null)
	{
		self::$_instance = $this;

		parent::__construct($environment, $options);
	}

    private function __clone()
    {
    }

	public static function getInstance()
	{
		return self::$_instance;
	}


	public function getSiteName()
	{
		$this->getOptions();
		return $this->_siteName;
	}
	public function getSiteTagline()
	{
		$this->getOptions();
		return $this->_siteTagline;
	}
	public function getSiteFooterNotice()
	{
		$this->getOptions();
		return $this->_siteFooterNotice;
	}


	public function setHeadTitle($string = NULL)
	{
		$this->_headTitle = $string;
		return $this;
	}

	public function getHeadTitle()
	{
		return $this->_headTitle;
	}


    public function bootstrap()
    {
		//CHECK: need this?
//		Zend_Session::start();

		Zend_Loader_Autoloader::getInstance()->registerNamespace(APPLICATION_NAMESPACE .'_');
//		Zend_Loader_Autoloader::getInstance()->registerNamespace('Core_');

		$zf_front = Zend_Controller_Front::getInstance();

		// Controller directory for the default module
		$zf_front->addControllerDirectory(CORE_PATH . '/controllers', 'default');

		$autoloader = new Zend_Application_Module_Autoloader(array(
			'namespace' => APPLICATION_NAMESPACE,
			'basePath'  => CORE_PATH,
		));

		$this->initDb();


		// Do some re-routes
		$router = Zend_Controller_Front::getInstance()->getRouter();

		// Re-route login
		$router->addRoute('login',
			new Zend_Controller_Router_Route('login/*',
				array('module' => 'account', 'controller' => 'auth', 'action' => 'login'))
		);
		// Re-route logout
		$router->addRoute('logout',
			new Zend_Controller_Router_Route('logout/*',
				array('module' => 'account', 'controller' => 'auth', 'action' => 'logout'))
		);
		$router->addRoute('account_routes',
			new Zend_Controller_Router_Route('account/:action/*',
				array('module' => 'account', 'controller' => 'index', 'action' => 'index'))
		);

		// Routing for module administrations
		$router->addRoute('admin_module',
			new Zend_Controller_Router_Route('admin/module/:module_idname/:action/*',
				array('module' => 'admin', 'controller' => 'modules', 'action' => 'index'))
		);
		// Routing for widget administrations
		$router->addRoute('admin_widget',
			new Zend_Controller_Router_Route('admin/widget/:widget_idname/:action/*',
				array('module' => 'admin', 'controller' => 'widgets', 'action' => 'index'))
		);


		$this->initComponents();


//		Euso_ObjectClass::register('user', 'Account_Model_Account', NULL, 'default');


		$this->loadModules();
		$this->initModuleAutoloaders();


		parent::bootstrap();


        return $this;
    }

	// Override
	public function getOptions()
	{
		if ($this->_dbInitialized === TRUE && $this->_optionsDbLoaded === FALSE) {
			$this->loadOptionsFromDb();
			$this->_optionsDbLoaded = TRUE;
		}
		return parent::getOptions();
	}


	public function isModuleExists($idname = NULL)
	{
		if (empty($idname) === FALSE
			&& isset($this->_moduleList[$idname])) {
			return TRUE;
		}
		return FALSE;
	}
	public function isModuleActive($idname)
	{
		if (empty($idname) === FALSE
			&& isset($this->_moduleList[$idname])
			&& $this->_moduleList[$idname]->getActive() === TRUE) {
			return TRUE;
		}
		return FALSE;
	}
	public function getModule($idname, $activeOnly = TRUE)
	{
		if (empty($idname) === FALSE
			&& isset($this->_moduleList[$idname])
			&& ($activeOnly === FALSE || $this->_moduleList[$idname]->getActive() === TRUE)) {
			return $this->_moduleList[$idname];
		}
		return NULL;
	}
	public function getModuleList()
	{
		return $this->_moduleList;
	}

	public function isWidgetActive($idname)
	{
		if (empty($idname) === FALSE
			&& isset($this->_widgetList[$idname])
			&& $this->_widgetList[$idname]->getActive() === TRUE) {
			return TRUE;
		}
		return FALSE;
	}
	public function getWidget($idname, $activeOnly = TRUE)
	{
		if (empty($idname) === FALSE
			&& isset($this->_widgetList[$idname])
			&& ($activeOnly === FALSE || $this->_widgetList[$idname]->getActive() === TRUE)) {
			return $this->_widgetList[$idname];
		}
		return NULL;
	}
	public function getWidgetList()
	{
		return $this->_widgetList;
	}


	private function initDb()
	{
		//TODO: display nice message if the config not found
		// Check if the config is valid
		//CHECK: move to Euso_Db_Table_Abstract ?

		if (file_exists(CONFIG_PATH .'/db.php') === FALSE) {
			header("HTTP/1.0 500 Internal Server Error");
			header("Status: 500 Internal Server Error");
			echo '<HTML>';
			echo '<HEAD><TITLE>'. APPLICATION_NAME .' - ERROR</TITLE></HEAD>';
			echo '<BODY><H1>'. APPLICATION_NAME .' - ERROR</H1><P>Invalid configuration.</P></BODY>';
			echo '</HTML>';
			exit;
		}

		require_once CONFIG_PATH .'/db.php';

		Euso_Db_Table_Abstract::setTablePrefix($dbConfig['prefix']);

		$dbAdapter = Zend_Db::factory(
			'PDO_MYSQL', // hardcoded: MySQL only for now
			$dbConfig
		); 
		Euso_Db_Table_Abstract::setDefaultAdapter($dbAdapter);

		$this->_dbInitialized = TRUE;
	}

	private function loadOptionsFromDb()
	{
		$options = parent::getOptions();

		$optionsDbTable = new Euso_Model_DbTable_Options();

		$dbResult = $optionsDbTable->fetchAll('autoload="1"');
		foreach ($dbResult as $dbRow) {
			if (empty($dbRow->section) === FALSE) {
				$options[$dbRow->section] = $this->_processConfigKey($options[$dbRow->section],
					strval($dbRow->option_key), strval($dbRow->option_value));
			} else {
				$options = $this->_processConfigKey($options, strval($dbRow->option_key), strval($dbRow->option_value));
			}
		}

		$this->setOptions($options);

		// Site name and site tagline
		if (isset($options['site']) === TRUE) {
			if (isset($options['site']['name']) === TRUE) {
				$this->_siteName = $options['site']['name'];
			}
			if (isset($options['site']['tagline']) === TRUE) {
				$this->_siteTagline = $options['site']['tagline'];
			}
			if (isset($options['site']['footer']) && isset($options['site']['footer']['notice'])) {
				$this->_siteFooterNotice = $options['site']['footer']['notice'];
			}
		}
	}


	private function initComponents()
	{
		$widget = new Euso_Widget();
		$widget
			->setIdName('login')
			->setName('Login')
			->setOwner('default')
			->setActive(true)
			->setModule('account')
			->setController('widget')
			->setAction('login')
			->setParams(array())
		;
		$this->_widgetList['login'] = $widget;


		$moduleEntries = array();

		// Load the registry from the database
		{
			$componentDbTable = new Euso_Model_DbTable_ModuleRegistry();

			$dbResult = $componentDbTable->fetchAll();
			foreach ($dbResult as $dbRow) {
				$comp = array();
				$comp['idname'] = $dbRow->idname;
				$comp['version'] = $dbRow->version;
				$comp['active'] = intval($dbRow->active) === 1 ? TRUE : FALSE;
				$moduleEntries[$dbRow->idname] = (object)$comp;
			}
		}

		$widgetEntries = array();

		// Load the registry from the database
		{
			$componentDbTable = new Euso_Model_DbTable_WidgetRegistry();

			$dbResult = $componentDbTable->fetchAll();
			foreach ($dbResult as $dbRow) {
				$comp = array();
				$comp['idname'] = $dbRow->idname;
				$comp['version'] = $dbRow->version;
				$comp['active'] = intval($dbRow->active) === 1 ? TRUE : FALSE;
				$comp['module'] = $dbRow->module;
				$widgetEntries[$dbRow->idname] = (object)$comp;
			}
		}


        $moduleDirs = array();

		// Iterate the modules directory
		$dirit = new DirectoryIterator(MODULE_PATH);
		foreach ($dirit as $dir) {
			if ($dir->isDot() === FALSE && $dir->isDir() === TRUE) {
				$moduleDirs[] = $dir->getBasename();
			}
		}


		foreach ($moduleDirs as $dir)
		{
			// Load module
			$module_ini = MODULE_PATH .'/'. $dir .'/module.ini';
			if (file_exists($module_ini) === TRUE)
			{
				$idname = $dir;

				//TODO: check for conflicts (name conflict and owner conflict)

				//TODO: staging
				$moduleOptions = new Zend_Config_Ini($module_ini, 'module');
				if (isset($moduleOptions) === FALSE) {
					continue;
				}

				$module = new Euso_Module();
				$module
					->setIdName($idname)
					->setActive(FALSE)
					->setName($moduleOptions->name)
					->setNamespace($moduleOptions->namespace)
					->setVersion($moduleOptions->version)
					->setDescription($moduleOptions->description)
					->setConfigs($moduleOptions)
				;

				if (isset($moduleEntries[$idname])) {
					$module->setActive(intval($moduleEntries[$idname]->active) === 1);
					$module->setInstalledVersion($moduleEntries[$idname]->version);
				}

				$this->_moduleList[$idname] = $module;
			}

			// Don't continue if the module is not active
			if (isset($module) === FALSE || $module->getActive() === FALSE) {
				continue;
			}

			// Load widgets
			$widgets_ini = MODULE_PATH .'/'. $dir .'/widgets.ini';
			if (file_exists($widgets_ini) === TRUE)
			{
				$widgetsCfg = new Zend_Config_Ini($widgets_ini);
				$widgetList = $widgetsCfg->toArray();

				foreach ($widgetList as $idname => $wgt) {
					//TODO: check for conflicts (name conflict and owner conflict)

					$widget = new Euso_Widget();
					$widget
						->setOwner($dir)
						->setActive(false)
						->setIdName($idname)
						->setClass($wgt['class'])
						->setName($wgt['name'])
						->setModule($wgt['module'])
						->setVersion($wgt['version'])
						->setDescription($wgt['description'])
						->setController($wgt['controller'])
						->setAction($wgt['action'])
						->setParentName($wgt['parent'])
						->setParams($wgt['params'])
					;

					if (isset($widgetEntries[$idname])) {
						$widget->setActive(intval($widgetEntries[$idname]->active) === 1);
					}

					$this->_widgetList[$idname] = $widget;
				}
			}
		}

		//TODO: load params and configs from Db for active widgets
	}

	private function initModuleAutoloaders()
	{
		$zf_front = Zend_Controller_Front::getInstance();

		// Account module
		$zf_front->addControllerDirectory(CORE_PATH .'/account/controllers', 'account');
		new Zend_Application_Module_Autoloader(array(
			'namespace' => 'Account',
			'basePath'  => CORE_PATH .'/account',
		));
		// Administration module
		$zf_front->addControllerDirectory(CORE_PATH .'/admin/controllers', 'admin');
		new Zend_Application_Module_Autoloader(array(
			'namespace' => 'Admin',
			'basePath'  => CORE_PATH .'/admin',
		));

		foreach ($this->_moduleList as $module)
		{
			if ($module->active === TRUE) {
				if (empty($module->configs->controllerDirectory) === FALSE) {
					$zf_front->addControllerDirectory(MODULE_PATH .'/'. $module->idname
						.'/'. $module->configs->controllerDirectory, $module->idname);
				}

				$loader = new Zend_Application_Module_Autoloader(array(
					'namespace' => $module->namespace,
					'basePath'  =>  MODULE_PATH .'/'. $module->idname,
				));
			}
		}
	}

	/**
	 * Loads for active modules
	 */
	private function loadModules()
	{
		foreach ($this->_moduleList as $module) {
			if ($module->active === TRUE) {
				// Load the module's entry script
				@include MODULE_PATH .'/'. $module->idname .'/module.php';
			}
		}
	}


    // This method was stolen from Zend_Config_Ini
    private function _processConfigKey($config, $key, $value)
    {
        if (strpos($key, '.') !== false) {
            $pieces = explode('.', $key, 2);
            if (strlen($pieces[0]) && strlen($pieces[1])) {
                if (!isset($config[$pieces[0]])) {
                    if ($pieces[0] === '0' && !empty($config)) {
                        // convert the current values in $config into an array
                        $config = array($pieces[0] => $config);
                    } else {
                        $config[$pieces[0]] = array();
                    }
                } elseif (!is_array($config[$pieces[0]])) {
                    /**
                     * @see Zend_Config_Exception
                     */
                    require_once 'Zend/Config/Exception.php';
                    throw new Zend_Config_Exception("Cannot create sub-key for '{$pieces[0]}' as key already exists");
                }
                $config[$pieces[0]] = $this->_processConfigKey($config[$pieces[0]], $pieces[1], $value);
            } else {
                /**
                 * @see Zend_Config_Exception
                 */
                require_once 'Zend/Config/Exception.php';
                throw new Zend_Config_Exception("Invalid key '$key'");
            }
        } else {
            $config[$key] = $value;
        }
        return $config;
    }

}
